home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
IRIX 6.2 Applications 1996 May
/
SGI IRIX 6.2 Applications 1996 May.iso
/
dist
/
impr_dev.idb
/
usr
/
impressario
/
src
/
drivers
/
phandler
/
phandler.c.z
/
phandler.c
Wrap
C/C++ Source or Header
|
1996-05-06
|
37KB
|
1,086 lines
/**************************************************************************
*
* Copyright (c) 1992 Silicon Graphics, Inc.
* All Rights Reserved
*
* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF SGI
*
* The copyright notice above does not evidence any actual of intended
* publication of such source code, and is an unpublished work by Silicon
* Graphics, Inc. This material contains CONFIDENTIAL INFORMATION that is
* the property of Silicon Graphics, Inc. Any use, duplication or
* disclosure not specifically authorized by Silicon Graphics is strictly
* prohibited.
*
* RESTRICTED RIGHTS LEGEND:
*
* Use, duplication or disclosure by the Government is subject to
* restrictions as set forth in subdivision (c)(1)(ii) of the Rights in
* Technical Data and Computer Software clause at DFARS 52.227-7013,
* and/or in similar or successor clauses in the FAR, DOD or NASA FAR
* Supplement. Unpublished - rights reserved under the Copyright Laws of
* the United States. Contractor is SILICON GRAPHICS, INC., 2011 N.
* Shoreline Blvd., Mountain View, CA 94039-7311
**************************************************************************
*
* File:
* phandler.c:
*
* Description:
* Checks the status of the built-in parallel port, while passing data
* through to the built-in parallel port, buffering an optional # of bytes,
* and updating the printer status database as it goes.
*
* Can also be called to update status of the pod
* file ~lp/pod/<printername.status> and then return without sending
* and data to parallel port.
*
* See Also:
* Developers will want to be familiar with the SGI filter/driver spec
* to understand exactly which options their drivers MUST implement,
* how options are to be parsed, and which option letters are reserved.
*
* Developers may be well advised to read the following man pages
* for more information on the libraries and functions used in
* this program:
*
* libpod -- general information on POD database library
* plp -- information on the builtin parallel port interface
*
* Specific libpod function calls are also documented, see their man pages.
*
**************************************************************************/
#ident "$Revision: 1.38 $"
#include "phandler.h"
#include "impr_versions.h"
/* Variables set by command line options */
static short exit_on_error = FALSE; /* Exit on first error or retry? */
static short status_only = FALSE; /* Exit after reporting status? */
static short ignore_plp = FALSE; /* Ignore plp port status? */
static short print_warnings = TRUE; /* Report warnings in status? */
static short reset_port = FALSE; /* Reset plp before writing? */
static short ignore_EOI = TRUE; /* Ignore the EOI status bit? */
static short ignore_interrupts = FALSE;
static short DEBUG = 0; /* What level debugging is turned on: */
/* 0-3==none,COARSE,MEDIUM,FINE DETAIL*/
static char *printername; /* printer name (required cmd line arg) */
static char *progname; /* Name of this program when run, less path */
/* I/O buffer and sizes thereof */
#define BUFDEFSIZE 1024*1024
static int inbufsize = BUFDEFSIZE; /* size of buffer to allocate */
static char *inbuf = NULL; /* start of input data buffer */
static int inbytesleft; /* number of bytes left to read */
static pid_t child_pid = -1; /* process if of forked child */
/* Named paths to essential files and output port */
static FILE *errfile = stderr; /* log output to stderr or file */
static FILE *infile = stdin; /* input from stdin or file */
static short page_size_set = FALSE;
static int user_page_size = DEFAULT_PAGE_SIZE;
static int send_ctrl_d = FALSE; /* If true, send ctrlD at EOJ */
/* Printer POD information database structures */
static PDInfoStruct *pod; /* printer object database struct*/
static time_t podmodtime; /* time of last POD modification */
static PDMessageStruct errorMessages[PD_MESSAGE_MAX]; /* Error messages */
/***********************************************************************/
void set_program_name(char* name)
/* REQUIRES: nothing
* MODIFIES: global var progname
* EFFECTS: Sets global name of program (progname).
* Strips off any included path and leaves real name.
*/
{
if ((progname = strrchr(name,'/')) != NULL) progname++;
else progname = name;
}
/***********************************************************************/
void hold_signals(void) {
/* REQUIRES: nothing
* MODIFIES: signal handling environment
* EFFECTS: puts key signals on hold until release_signals is called.
*
* This function HOLDS the most common catchable interrupts.
* This is important in the UNIX environment because many
* software interrupts (signals) are possible.
*/
(void)sighold(SIGINT);
(void)sighold(SIGTERM);
(void)sighold(SIGHUP);
(void)sighold(SIGQUIT);
}
/***********************************************************************/
void release_signals(void) {
/* REQUIRES: nothing
* MODIFIES: signal handling environment
* EFFECTS: releases key signals from any hold condition
*
* This function RELEASES the most common catchable interrupts.
* This is important in the UNIX environment because many
* software interrupts (signals) are possible.
*/
if (ignore_interrupts && (child_pid != (pid_t) 0) ) return;
(void)sigrelse(SIGINT);
(void)sigrelse(SIGTERM);
(void)sigrelse(SIGHUP);
(void)sigrelse(SIGQUIT);
}
/***********************************************************************/
void cleanup_and_quit(int exitcode)
/* ASSUMES: Program should exit upon handling this error.
* MODIFIES: out
* EFFECTS: Cleans up printer and exits.
* Used to handle interrupts
*/
{
int ii;
/* Only called if a signal sent. Normal exit is through main.
* exception is the child process which exits here when signaled
* by parent process to quit.
*/
hold_signals();
if (child_pid == (pid_t) 0) { /* We are child */
if (DEBUG) {
fprintf(stderr,"%s: cleanup_and_quit called in child.\n",progname);
}
exit(exitcode);
}
if (DEBUG) {
fprintf(stderr,"%s: cleanup_and_quit called in parent.\n",progname);
}
/* Drain any remaining input */
if (inbuf) {
while (fgets(inbuf,inbufsize,infile))
;
}
/* If the port status is failed we need to hang around until it
* is OK. Imagine case were printer goes down and we changed the
* printer icon to a failed printer. The user then uses cancel to
* cancel the job sending us this interrupt we are handling. If we
* return the spooler will send out the next job (if there is one).
* Or, if there is no next job we leave the printer status as
* unavailable causing the icon to be the unavialable icon -- but when
* printer is re-connected the icon will not be updated (unless we
* continue to monitor it here).
*/
(void) update_status(PD_STATUS_IDLE);
if (!ignore_plp) {
while (pod->active_status->operational_status != PD_STATUS_IDLE) {
if (DEBUG) {
fprintf(stderr,"%s: Waiting for printer error to clear.\n",
progname);
}
(void) sleep(pod->error_retry_wait);
(void) update_status(PD_STATUS_IDLE);
}
if (inbuf) {
if (send_ctrl_d) {
inbuf[0]='\004';
inbuf[1]='\0';
(void) fwrite(inbuf,1,1,stdout);
}
}
}
exit(exitcode);
}
/***********************************************************************/
void setup_signal_handler(void) {
/* REQUIRES: nothing
* MODIFIES: signal handling environment
* EFFECTS:
*
* Sets up signal handler to catch the killing signals
* so that proper cleanup can be done
*
* This is important in the UNIX environment because many
* software interrupts are possible. We catch the most common
* catchable interrupts.
*
* Cleanup_and_quit is a void(int ...) function which
* should clean up the printer and then exit the program.
*/
(void) signal(SIGINT, cleanup_and_quit);
(void) signal(SIGTERM, cleanup_and_quit);
(void) signal(SIGHUP, cleanup_and_quit);
(void) signal(SIGQUIT, cleanup_and_quit);
}
/***********************************************************************/
void print_usage_message(void)
/* REQUIRES: nothing
* MODIFIES: errfile
* EFFECTS: prints usage message
*/
{
fprintf(errfile, "\nUSAGE:\n %s", progname);
fprintf(errfile, "\t-P Printer name. (REQUIRED)\n");
fprintf(errfile, "\t\t[-e] Exit immediately on error.\n");
fprintf(errfile, "\t\t[-w] Suppress warning reporting.\n");
fprintf(errfile, "\t\t[-D] Debug mode, appends to logfile.\n");
fprintf(errfile, "\t\t[-L file] Log file for debugging info and errors.\n");
fprintf(errfile, "\t\t (Default log file is stderr.)\n");
fprintf(errfile, "\t\t[-R] Reset parallel port before sending data.\n");
fprintf(errfile, "\t\t[-K] Ignore Interrupts.\n");
fprintf(errfile, "\t\t[-I] Enable sensing of EOI pin, normally ignored.\n");
fprintf(errfile, "\t\t[-B int] Internal buffer size in bytes.\n");
fprintf(errfile, "\t\t[-s] Status-only invocation.\n");
fprintf(errfile, "\t\t Checks the parallel port and updates\n");
fprintf(errfile, "\t\t paper size (if specified)\n");
fprintf(errfile, "\t\t[-u] Do not check the parallel port status.\n");
fprintf(errfile, "\t\t Ignored if -s was NOT specified\n");
fprintf(errfile, "\t\t Allows driver to update pod status\n");
fprintf(errfile, "\t\t while ignoring parallel port\n");
fprintf(errfile, "\t\t[-S string] Paper Size (e.g., A, A4, Legal).\n");
fprintf(errfile, "\t\t If specified, the pod status file for\n");
fprintf(errfile, "\t\t the printer is updated.\n");
fprintf(errfile, "\t\t[-d] Send a CTRL-D (ASCII 004) at end of job.\n");
fprintf(errfile, "\t\t[filename] File to send, assumed <stdin> if none given.\n");
fprintf(errfile, "\n");
}
/***********************************************************************/
void process_args(int argc, char* argv[])
/*
* REQUIRES: argc, argv, optind in valid format
* MODIFIES: has side effects on global state, like DEBUG, files, etc..
*
* EFFECTS: Processes all the command line args using getopts.
* Sets a bunch of state depending on which args are passed.
*/
{
int c;
while ((c = getopt(argc, argv, "KdeuswDP:RIL:B:O:C:S:fp:r:z:mq:n:tc:i:o:")) != -1) {
switch (c) {
case 'K':
ignore_interrupts = TRUE;
break;
case 'd': /* Whether we exit immediately on error: default is to retry */
send_ctrl_d = TRUE;
break;
case 'e': /* Whether we exit immediately on error: default is to retry */
exit_on_error = TRUE;
break;
case 's': /* Should we exit immediately after updating status? */
status_only = TRUE;
break;
case 'u': /* Should we ignore plp port -- only use if -s specified? */
ignore_plp = TRUE;
break;
case 'S':
/* Attempt to convert this string to a page size. */
if ((user_page_size = PDGetSizeCodeByName(optarg)) < 0) {
/* If a PD error, then print that error code out */
if (PDerrno != 0) {
PDPerror(progname);
} else {
fprintf(stderr, "%s: Invalid page size '%s' selected.\n",
progname, optarg);
}
/* We failed. Fall back to our default page size. */
fprintf(stderr, "%s: Using default page size '%s'.\n",
progname, PDGetNameBySizeCode(DEFAULT_PAGE_SIZE));
user_page_size = DEFAULT_PAGE_SIZE;
}
page_size_set = TRUE;
break;
case 'w': /* Should we print warnings/info messages? Default is yes */
print_warnings = FALSE;
break;
case 'D': /* Turn on debugging logging */
DEBUG++; /* Multiple -D flags increment level of detail. */
/* Coarse, medium, and fine detail are invoked by
* one, two, and three or more -D flags respectively.
*/
switch (DEBUG) {
/* Print a message to logfile if debug > 0 */
case 0:
break;
case COARSEDETAIL:
fprintf(errfile,"%s: Coarse detail debug messages enabled.\n",
progname);
break;
case MEDIUMDETAIL:
fprintf(errfile,"%s: Medium detail debug messages enabled.\n",
progname);
break;
case FINEDETAIL:
default:
fprintf(errfile, "%s: Fine detail debug messages enabled.\n",
progname);
break;
}
break;
case 'P': /* REQUIRED: name of printer as installed under SysV spooler */
printername = strdup(optarg);
break;
case 'R': /* Whether we should reset the parallel port.
* This is not a required option, just one added for this driver
*/
reset_port = TRUE;
break;
case 'I': /* Whether we should ignore EOI, default is TRUE.*/
ignore_EOI = FALSE;
break;
case 'L': /* Where to write logging information. Default is stderr */
if (!(errfile = fopen(optarg,"a"))) {
errfile = stderr;
fprintf(errfile,"%s:\tUnable to open logfile %s for writing.\n",
progname, optarg);
fprintf(errfile,"%s:\tUsing standard error instead.\n",
progname);
}
break;
case 'B': /* How big a buffer to allocate for data */
if (atoi(optarg) <= 0)
fprintf(errfile,
"%s: -%c (buffer size) option must have a positive argument.\n",
progname, (char)c);
else inbufsize = atoi(optarg);
break;
/* The following options are those which this driver either does not
* or cannot implement, due to limitations on its intelligence
* about the input format and printer format.
*
* They are reproduced here for your convenience, so that
* when you the developer implement the full set you have a
* checklist here for your use.
*/
case 'O': /* Where to write our output data. */
case 'f': /* flip input image */
case 'p': /* scale to match a given pixels-per-inch input resolution */
case 'r': /* rotate the image, an integer number of degrees */
case 'z': /* zoom the image, a floating point argument */
case 'm': /* whether to request manual feed */
case 'q': /* quality mode, an integer */
case 'n': /* number of copies, an integer, model file handles this */
case 't': /* print a test page */
case 'o': /* output media type, for color matching */
fprintf(errfile,
"%s: -%c option not handled by this driver, ignoring it.\n",
progname, (char)c);
break;
case '?': /* getopts didn't recognize this option from list above */
print_usage_message();
exit(2);
}
}
/* ignore_plp can only be true when status_only is true */
if (!status_only) ignore_plp = FALSE;
/* was a printer name specified? If not, exit */
if (printername == NULL) {
fprintf(errfile, "%s: Printer name argument required:\n", progname);
fprintf(errfile, "%s: \tYou must use the -P option to specify the "
"printer name.\n", progname);
exit(2);
}
/* Was a filename specified after the last argument?
* If so, grab it and try to open it instead of stdin.
*/
if (optind < argc) {
fprintf(errfile, "%s: reading from %s\n", progname, argv[argc-1]);
infile = fopen(argv[argc-1], "r");
if (!infile) {
/* In future versions, we will use libpod to write errors to the
* printer's .log file. See libpod man page for PDWriteLog.
*/
fprintf(errfile, "%s: Could not open input file %s for reading\n",
progname, argv[argc-1]);
exit(1);
}
}
return;
}
/***********************************************************************/
int printer_error(int error_code)
/*
* EFFECTS:
* Appends one error to the global errorMessages array
* and increments global variable pod->error_count by one.
*
* This function adds an error code and corresponding message
* to the active error status. A message that matches the
* error code is added to the structure along with the error
* code.
*
* If the number of messages would be increased over PD_MESSAGE_MAX,
* then no message is added to the error_status struct.
*
* RETURNS:
* Returns the lesser of the error count and PD_MESSAGE_MAX.
*
* ASSUMPTIONS:
* The error status will be reset by setting error_count to zero
* before a new set of error status is reported.
* Set "pod->active_status->error_count = 0;"
*
* Assumes the following global variables:
* static PDInfoStruct *pod;
* static PDMessageStruct errorMessages[PD_MESSAGE_MAX];
*
*/
{
static char msgText[PD_STR_MAX]; /* enough memory for longest legal message */
if (pod->active_status->error_count >= PD_MESSAGE_MAX) {
return PD_MESSAGE_MAX;
}
switch (error_code) {
/* First we handle any special cases we want to report that
* the libpod library may not take care of for us in the fashion we desire.
* We'll demonstrate the safer, easier default method below.
*/
case PD_MSG_MASK_VERSION:
/*
* Case in point: The version message is not handled in libpod,
* because it requires application-specific information.
* In this case we'll make our own message.
*/
(void) sprintf(msgText, "%s", PhandlerDriverVersion);
/* We've got our string. Now let's put it into errorMessages.
* Do this very safely so developers can change the above at will.
*/
errorMessages[pod->active_status->error_count].message_code = error_code;
/* Copy only as many characters as are guaranteed to fit, less the null */
(void) strncpy(errorMessages[pod->active_status->error_count].message_text,
msgText, PD_STR_MAX - 1 );
/* Now make sure to put a null at the end of the string. */
errorMessages[pod->active_status->error_count].message_text[PD_STR_MAX-1]
= '\0';
break;
default:
/*
* Now that we've handled any special cases, pass the rest off to the
* default handler supplied by libpod. This is the easiest and safest
* method, but we had to demonstrate both, now didn't we?
*/
PDMakeMessage(&errorMessages[pod->active_status->error_count], error_code);
break;
}
/* NOTE: *DO NOT INCREMENT ERROR_COUNT IN THE CODE ABOVE*
* unless you absolutely MUST add multiple messages at once,
* because pod->error_count will be incremented by one on exit.
*
* Instead of adding multiple messages at once, you should just call
* printer_error multiple times, once with each message you wish.
*/
return ++pod->active_status->error_count;
}
/***********************************************************************/
int update_status(int cur_status) {
/* REQUIRES: parallel port open for reading
* cur_status is what the printer status SHOULD be,
* barring other errors....
* MODIFIES: active status file, errfile
* EFFECTS: reads parallel port status and reports it into
* printer active status file.
* ASSUMES: We will not report more than PD_ERROR_MAX errors.
* RETURNS: TRUE if reading status was successful, else FALSE.
*/
int iostat; /* the I/O port status byte, per plp.h */
pid_t parent_pid;
/* IMPORTANT: Caller should hold signals when called. */
/* Set the status to be the cur_status. If there are more pressing issues
* to report, the default status indicated in cur_status will be
* overridden below.
*/
pod->active_status->operational_status = cur_status;
if (!ignore_plp) {
/* Read the status byte from the parallel port */
while ((iostat = ioctl(fileno(stdout), PLPIOCSTATUS, 0)) == -1) {
fprintf(errfile,
"%s: Could not read status from parallel port (filedes #%d.)",
progname, fileno(stdout));
if (exit_on_error) {
fprintf(errfile, " Exiting after iostat error on "
" parallel port\n");
return TRUE;
} else {
fprintf(errfile," Retrying in %d seconds....\n",
pod->error_retry_wait);
(void) sleep(pod->error_retry_wait);
}
}
/* Interpret that byte: */
/* Decide whether to ignore the PLPEOI bit, depending on cmd
* line switches. It is almost always high because few printer
* manufacturers obey the centronics protocol to report this
* bit, and they generally simply wire this pin to chassis
* ground.
*/
if (ignore_EOI) {
iostat &= ~PLPEOI; /* mask out the EOI pin, it's often invalid */
}
if (DEBUG) {
fprintf(errfile, "%s: Status = %d\n", progname, iostat);
}
/* Set up to write the active status file error messages.
* Reset the error_count to start over with new messages.
*/
pod->active_status->error_count = 0;
switch(iostat) {
case (PLPONLINE):
/* Printer is online, all status OK. Make
* operational_status the state indicated by the passed in
* argument cur_status, which varies depending on where in
* the program we are.
*/
pod->active_status->operational_status = cur_status;
break;
case ( 0 ):
/* Printer is offline, all other status OK */
(void) printer_error(PD_ERROR_OFFLINE);
pod->active_status->operational_status = PD_STATUS_FAULTED;
break;
case (PLPEOP):
/* Printer is offline and out of paper */
(void) printer_error(PD_ERROR_PAPER_OUT);
(void) printer_error(PD_ERROR_OFFLINE);
pod->active_status->operational_status = PD_STATUS_FAULTED;
break;
case (PLPFAULT):
/* Printer is faulted, no specific reason given. */
(void) printer_error(PD_ERROR_OFFLINE);
(void) printer_error(PD_ERROR_OTHER_FAULT);
pod->active_status->operational_status = PD_STATUS_FAULTED;
break;
case (PLPFAULT + PLPEOP):
/* Printer is faulted and out of paper */
(void) printer_error(PD_ERROR_PAPER_OUT);
(void) printer_error(PD_ERROR_OTHER_FAULT);
pod->active_status->operational_status = PD_STATUS_FAULTED;
break;
case (PLPEOI):
case (PLPFAULT + PLPEOI):
(void) printer_error(PD_ERROR_INK_OUT);
pod->active_status->operational_status = PD_STATUS_FAULTED;
break;
case (PLPONLINE + PLPEOP):
case (PLPONLINE + PLPEOI):
case (PLPFAULT + PLPONLINE + PLPEOP):
case (PLPFAULT + PLPONLINE):
/*
* All these cases are invalid combinations.
* We assume that these mean there is no printer connected.
*
* These bits may go high when you pull the cable loose,
* or turn off the printer.
*/
(void) printer_error(PD_ERROR_NOT_RESPONDING);
pod->active_status->operational_status = PD_STATUS_UNAVAILABLE;
break;
default:
/* unknown combination .... report "other fault". We
* shouldn't reach this statement, but good programming
* convention demands that always include a default case in
* every switch statement!
*/
(void) printer_error(PD_ERROR_OTHER_FAULT);
pod->active_status->operational_status = PD_STATUS_UNAVAILABLE;
break;
}
} /* if (!ignore_plp) */
/* If warnings info is allowed, add the optional driver version now. */
if (print_warnings) {
(void) printer_error(PD_MSG_MASK_VERSION);
}
/* Set the page size to whatever the user has requested. */
if (page_size_set) {
pod->active_status->media_size = user_page_size;
}
/* Caller should hold signals so we won't interrupt any changes to
* the pod database in mid-change.
*/
if (DEBUG) {
fprintf(stderr,"%s: Updating printer POD status file.\n", progname);
}
PDLocalWriteStatus(printername, pod->active_status, errorMessages);
return TRUE;
}
/***********************************************************************/
void reset_plp(void)
{
if (DEBUG) {
fprintf(errfile, "%s: Resetting parallel port...\n", progname);
}
while (ioctl(fileno(stdout), PLPIOCRESET, 0) == -1) {
fprintf(errfile,"%s: Could not reset the parallel port.", progname);
if (exit_on_error) {
fprintf(errfile,"Exiting.\n");
exit(1);
} else {
/* Retry indefinitely until successful or KILLed. */
fprintf(errfile,"Retrying in %d seconds....\n",
pod->error_retry_wait);
(void) sleep(pod->error_retry_wait);
}
}
}
/***********************************************************************/
void open_plp(void)
{
/* Open port.
*
* Exit only if error exit is requested on command line.
* If open fails, issue one message and wait indefinitely.
*
* IMPORTANT PHILOSOPHICAL NOTE:
*
* If exit_on_error has not been set, DO NOT EXIT *EVER* unless killed.
* This ensures that no job falls off the end of the queue without some
* sort of printed output, or an explicit job cancellation.
*
* This philosophy is consistent throughout, so adhere to it!
*/
if (DEBUG > MEDIUMDETAIL) {
fprintf(errfile, "%s: Opening parallel port '%s'.\n",
progname, pod->port_path);
}
/* Compare only first 8 charaters to make sure it is a parallel port.
* Valid ports are /dev/plp[n]. Larger systems can have multiple
* parallel ports. See plp(7) man page.
*/
if (strncmp(pod->port_path, "/dev/plp",8)) {
fprintf(errfile, "%s: Device '%s' specified in config file is not "
"supported: Exiting.\n", progname, pod->port_path);
fprintf(errfile, "%s: Please modify config file to list /dev/plp as "
"the printer connection device.", progname);
exit(1);
}
while (freopen(pod->port_path, "w", stdout) == NULL) {
/* If we got here, we have an error while opening the port */
if (exit_on_error) { /* Exit immediately. */
/* Print a message in the common case where the port was busy. */
if (errno == EBUSY) {
fprintf(errfile,
"%s: Output port %s busy -- cannot open. Exiting...\n",
progname, pod->port_path);
exit(1);
} else {
fprintf(errfile,
"%s: Couldn't open output port %s, error #%d. Exiting.\n", progname, pod->port_path, errno);
exit(1);
}
} else {
/*
* Exit_on_error not set:
* Loop indefinitely trying to open the port,
* checking every pod->error_retry_wait seconds.
*/
fprintf(errfile, "%s: Output port %s could not be opened "
"(error #%d). Retrying in %d seconds...\n",
progname, pod->port_path, errno, pod->error_retry_wait);
(void) sleep(pod->error_retry_wait);
}
}
/* ASSERTION: If we got here, we successfully opened the parallel port. */
if (DEBUG > MEDIUMDETAIL) {
fprintf(errfile, "%s: Successfully opened parallel port. Now trying "
"to lock port....\n", progname);
}
/* Put an advisory file lock on the port. First we try a
* nonblocking lock so we can report whether our first try
* succeeded. If it failed because it would have blocked, report
* that and try a blocking lock. We report so that a blocked lock
* can be diagnosed by the user.
*/
if (flock(fileno(stdout), LOCK_EX | LOCK_NB) < 0) {
fprintf(stderr,"%s: Error #%d while locking parallel port.\n",
progname, errno);
if (errno != EWOULDBLOCK) {
perror(progname);
exit(1);
} else {
/* errno == EWOULDBLOCK: someone else has it locked already.
* report that someone has it locked, then do a blocking lock.
*/
/* In future versions, we will use libpod to write to .log file.
* See libpod man page, specifically for PDWriteLog.
*/
fprintf(errfile, "%s: Output port locked by another process. "
"Waiting for lock.\n", progname);
if (flock(fileno(stdout), LOCK_EX) < 0) {
perror(progname);
exit(1);
}
}
}
/* ASSERTION: if we got here, our lock succeeded. We now own the port. */
if (DEBUG > MEDIUMDETAIL) {
fprintf(errfile, "%s: Successfully locked parallel port.\n", progname);
}
}
/***********************************************************************/
main(int argc, char** argv)
{
short empty_infile = TRUE;
/* set the progname variable to the stripped program name */
set_program_name( argv[0] );
/* Catch the killing signals so that proper cleanup can be done
* This is important in the UNIX environment because many
* software interrupts are possible. We catch the most common
* catchable interrupts. Cleanup_and_quit is a void(int ...) func which
* should clean up the printer, and then exit the program.
*/
(void) setup_signal_handler();
/* Parse command line options */
process_args( argc, argv );
/* Open the POD database files. */
if (PDLocalReadInfo(printername, &pod, &podmodtime) < 0) {
fprintf(errfile, "%s: Could not open required POD database files "
"for printer %s.\n", progname, printername);
fprintf(errfile, "%s: Are you sure all required POD files are "
"properly installed?\n", progname);
PDPerror(progname);
exit(2);
}
if (DEBUG > MEDIUMDETAIL) {
fprintf(errfile, "%s: Successfully read printer POD files.\n", progname);
}
if (!ignore_plp) {
(void) open_plp();
}
/* Update status. If status-only is true, exit after updating status. */
hold_signals();
if (status_only == TRUE) {
/* Then update the status with idle default
* (since we're exiting immediately) and then exit. */
exit(update_status(PD_STATUS_IDLE) == FALSE);
} else {
(void) update_status(PD_STATUS_BUSY);
}
/* Now reset the parallel port if that was requested via cmd line option. */
if (!ignore_plp && reset_port) {
(void) reset_plp();
}
/* Allow signals to interrupt from here on. See cleanup_and_quit function
* to see how signals are hndled.
*/
release_signals();
/* Allocate and set up the input buffer */
if (inbufsize < 128) inbufsize = BUFDEFSIZE;
inbuf = (char *)malloc(inbufsize);
if (inbuf == NULL) {
fprintf(errfile,"%s: Could not allocate requested buffer of %d bytes.\n",
progname, inbufsize);
exit(1);
}
if (DEBUG > COARSEDETAIL) {
fprintf(errfile,"%s: Allocated buffer of %d bytes.\n",progname,inbufsize);
}
/* Now read status, update status db, and send data. We don't want
* to block status updating waiting for i/o. We also want to keep
* the internal buffer (if any) as full as possible while sending
* data. If infile is empty, exit with an error code.
*/
/* To avoid blocking, let's fork and have a child do the status
* updating while the main process does its reading and writing.
*/
(void) signal(SIGCLD, SIG_IGN); /* ignore child death, don't make zombies */
if ( (child_pid = fork()) == (pid_t) -1 ) {
fprintf(stderr, "%s: can't fork child process to update status, "
"status may be inaccurate", progname);
}
/*
* NOTE TO DEVELOPER:
* If you don't understand the implications of forking multiple processes,
* please read at least the fork() man page.
*/
if (child_pid == (pid_t) 0) { /* If pid == 0, we're the child process. */
/* Register ourselves to get a SIGHUP when the parent process dies. */
(void)prctl(PR_TERMCHILD);
if (DEBUG) {
fprintf(stderr, "%s: Forked a child to read status.\n", progname);
}
/* Now just keep updating status, sleeping between polls. */
while (1) {
hold_signals();
(void) update_status(PD_STATUS_BUSY);
release_signals();
(void) sleep(pod->error_retry_wait);
}
/* NOTREACHED IF CHILD */
}
/* We are the parent process, do the rest of the normal thing you'd do.
*
* NOTE: SIgnals are off in this loop -- it can be interrupted
*/
while(!feof(infile)) {
/* read and update the status if no child watching status */
if (child_pid == (pid_t) -1) {
hold_signals();
(void) update_status(PD_STATUS_BUSY);
release_signals();
}
inbytesleft = fread(inbuf, 1, inbufsize, infile);
if (inbytesleft > 0) empty_infile = FALSE;
if (ferror(infile)) {
fprintf(errfile, "%s: Error #%d reading input pipe, exiting.\n",
progname, errno);
if (child_pid != (pid_t) -1) {
(void) kill(child_pid, SIGHUP);
(void) sleep(1);
}
hold_signals();
(void) update_status(PD_STATUS_FAULTED);
exit(errno);
}
/*
* NOTE TO DEVELOPERS:
*
* Insert subroutine here to process the input data
* from intermediate format to printer format.
*
* phandler does not do this since it is not a printer-specific
* driver, and is only an example. Your driver will know
* both what kind of printer it is driving
* and what kind of data it is receiving.
*
* Therefore you will want to do data-specific processing here,
* which will in turn improve your error-recovery ability.
*/
(void) fwrite(inbuf,1,inbytesleft,stdout);
if (ferror(stdout)) {
int exitcode = errno;
fprintf(errfile, "%s: Error #%d writing output pipe.\n",
progname, exitcode);
if (child_pid != (pid_t) -1) {
(void) kill(child_pid, SIGHUP);
(void) sleep(1);
}
hold_signals();
(void) update_status(PD_STATUS_FAULTED);
exit(exitcode);
}
(void) fflush(stdout);
} /* end while */
if (inbuf) {
if (send_ctrl_d) {
inbuf[0]='\004';
inbuf[1]='\0';
(void) fwrite(inbuf,1,1,stdout);
}
}
/* Reset printer and exit, updating status on the way. */
hold_signals();
if (child_pid != (pid_t) -1) {
(void) kill(child_pid, SIGHUP);
(void) sleep(1); /* wait a while for child to go away */
}
/* We're done, reset state to idle */
(void) update_status(PD_STATUS_IDLE);
/* Now obey the convention that we exit error code 1 if no data was read.
* This ensures that shell scrips which call us as the last stage in a
* pipe can catch error exits of the filters upstream of us.
*/
if (empty_infile == TRUE) exit(1);
return 0;
}